package com.aim.analysis.records;

import com.aim.game.MC;
import com.aim.util.extractors.ValueExtractor;
import com.google.common.primitives.Ints;
import org.math.array.IntegerArray;

import java.util.ArrayList;
import java.util.List;

/**
 * User: Avraham Shvartzon
 * Date: 6/5/14
 */
public abstract class GameValueExtractor<V> extends ValueExtractor<Game, V> {

    public GameValueExtractor(String name) {
        super(name);
    }

    public final static GameValueExtractor<String> partitionKey = new GameValueExtractor<String>("PartitionKey") {
        @Override
        public String getValue(Game game) {
            return game.getKey().partitionKey;
        }
    };

    public final static GameValueExtractor<String> gameDescription = new GameValueExtractor<String>("gameDescription") {
        @Override
        public String getValue(Game game) {
            return game.getKey().gameDescription;
        }
    };

    public final static GameValueExtractor<Integer> gameNumber = new GameValueExtractor<Integer>("gameNumber") {
        @Override
        public Integer getValue(Game game) {
            return game.getKey().gameNumber;
        }
    };

    public final static GameValueExtractor<Integer> score = new GameValueExtractor<Integer>("Score") {
        @Override
        public Integer getValue(Game game) {
            return game.getFinalScore();
        }
    };

    public final static GameValueExtractor<Integer> maintenance = new GameValueExtractor<Integer>("Maintenance") {
        @Override
        public Integer getValue(Game game) {
            return game.numOfRecords(RecordType.maintenaneAction);
        }
    };

    public final static GameValueExtractor<Integer> minor = new GameValueExtractor<Integer>("Minor") {
        @Override
        public Integer getValue(Game game) {
            int numOfMinor = 0;
            List<GameRecord> records = game.getRecords(RecordType.repairAction);
            if (records != null && records.size() > 0){
                for (GameRecord record : records) {
                        if (record.mechanicalCondition == MC.M1){
                        numOfMinor++;
                    }
                }
            }
            return numOfMinor;
        }
    };

    public final static GameValueExtractor<Integer> moderate = new GameValueExtractor<Integer>("Moderate") {
        @Override
        public Integer getValue(Game game) {
            int num = 0;
            List<GameRecord> records = game.getRecords(RecordType.repairAction);
            if (records != null && records.size() > 0){
                for (GameRecord record : records) {
                    if (record.mechanicalCondition == MC.M2){
                        num++;
                    }
                }
            }
            return num;
        }
    };

    public final static GameValueExtractor<Integer> severe = new GameValueExtractor<Integer>("Severe") {
        @Override
        public Integer getValue(Game game) {
            int num = 0;
            List<GameRecord> records = game.getRecords(RecordType.repairAction);
            if (records != null && records.size() > 0){
                for (GameRecord record : records) {
                    if (record.mechanicalCondition == MC.M3){
                        num++;
                    }
                }
            }
            return num;
        }
    };

    public final static GameValueExtractor<Integer> totalShutdown = new GameValueExtractor<Integer>("NumOfShutdown") {
        @Override
        public Integer getValue(Game game) {
            return game.totalShutdown;
        }
    };

    public final static GameValueExtractor<Integer> totalCollision = new GameValueExtractor<Integer>("NumOfCollision") {
        @Override
        public Integer getValue(Game game) {
            return game.totalCollision;
        }
    };

    public final static GameValueExtractor<Integer> totalFire = new GameValueExtractor<Integer>("NumOfShots") {
        @Override
        public Integer getValue(Game game) {
            return game.totalFire;
        }
    };

    public final static GameValueExtractor<Integer> repairCost = new GameValueExtractor<Integer>("repairCost") {
        @Override
        public Integer getValue(Game game) {
            int repairCost = minor.getValue(game) * Scenario.instance().minorCost
                    + moderate.getValue(game) * Scenario.instance().moderateCost
                    + severe.getValue(game) * Scenario.instance().severeCost;
            return repairCost;
        }
    };

    public final static GameValueExtractor<Integer> repairTime = new GameValueExtractor<Integer>("repairTime(millis)") {
        @Override
        public Integer getValue(Game game) {
            int repairTime = minor.getValue(game) * Scenario.instance().minorTime
                    + moderate.getValue(game) * Scenario.instance().moderateTime
                    + severe.getValue(game) * Scenario.instance().severeTime;
            return repairTime;
        }
    };

    public final static GameValueExtractor<Integer> maintenanceCost = new GameValueExtractor<Integer>("maintenanceCost") {
        @Override
        public Integer getValue(Game game) {
            return maintenance.getValue(game) * Scenario.instance().maintenanceCost;
        }
    };

    public final static GameValueExtractor<Integer> maintenanceTime = new GameValueExtractor<Integer>("maintenanceTime(millis)") {
        @Override
        public Integer getValue(Game game) {
            return maintenance.getValue(game) * Scenario.instance().maintenanceTime;
        }
    };

    public final static GameValueExtractor<Integer> mechanicalCost = new GameValueExtractor<Integer>("mechanicalCost") {
        @Override
        public Integer getValue(Game game) {
            return maintenanceCost.getValue(game) + repairCost.getValue(game);
        }
    };

    public final static GameValueExtractor<Integer> mechanicalTime = new GameValueExtractor<Integer>("mechanicalTime(millis)") {
        @Override
        public Integer getValue(Game game) {
            return maintenanceTime.getValue(game) + repairTime.getValue(game);
        }
    };

    public final static GameValueExtractor<List<Integer>> refuelIntervals = new GameValueExtractor<List<Integer>>("refuelIntervals") {
        @Override
        public List<Integer> getValue(Game game) {
            List<Integer> intervals = new ArrayList<Integer>();

            List<GameRecord> records = game.getRecords(RecordType.fuelFill);
            if (records != null) {
                for (GameRecord record : records) {
                    int interval = (Scenario.instance().maxFuelInventory - record.fuelLevel)/1000; //in seconds
                    intervals.add(interval);
                }
            }
            return intervals;
        }
    };

    public final static GameValueExtractor<Integer> refuelByStations = new GameValueExtractor<Integer>("refuelByStations") {
        @Override
        public Integer getValue(Game game) {
            return game.numOfRecords(RecordType.fuelFill);
        }
    };

    public final static GameValueExtractor<Integer> refuelByStationsCost = new GameValueExtractor<Integer>("refuelByStationsCost") {
        @Override
        public Integer getValue(Game game) {
            int[] intervals = Ints.toArray(refuelIntervals.getValue(game));
            return IntegerArray.sum(intervals);
        }
    };

    public final static GameValueExtractor<Integer> refuelBySpaceship = new GameValueExtractor<Integer>("refuelBySpaceship") {
        @Override
        public Integer getValue(Game game) {
            return game.numOfRecords(RecordType.fuelFillBySpaceship);
        }
    };

    public final static GameValueExtractor<Integer> refuelBySpaceshipCost = new GameValueExtractor<Integer>("refuelBySpaceshipCost") {
        @Override
        public Integer getValue(Game game) {
            return refuelBySpaceship.getValue(game) * Scenario.instance().REFUEL_BY_SPACESHIP_COST;
        }
    };

    public final static GameValueExtractor<Integer> refuelBySpaceshipTime = new GameValueExtractor<Integer>("refuelBySpaceshipTime(millis)") {
        @Override
        public Integer getValue(Game game) {
            return refuelBySpaceship.getValue(game) * Scenario.instance().REFUEL_BY_SPACESHIP_TIME;
        }
    };

    public final static GameValueExtractor<Integer> totalRefuelCost = new GameValueExtractor<Integer>("totalRefuelCost") {
        @Override
        public Integer getValue(Game game) {
            return refuelBySpaceshipCost.getValue(game) + refuelByStationsCost.getValue(game);
        }
    };

    public final static GameValueExtractor<Integer> freezeTime = new GameValueExtractor<Integer>("freezeTime(millis)") {
        @Override
        public Integer getValue(Game game) {
            return refuelBySpaceshipTime.getValue(game) + mechanicalTime.getValue(game);
        }
    };

    public final static GameValueExtractor<Double> netScore = new GameValueExtractor<Double>("netScore (per second)") {
        @Override
        public Double getValue(Game game) {
            int scores = totalShutdown.getValue(game) * Scenario.instance().shutDownScore
                    - totalCollision.getValue(game) * Scenario.instance().collisionCost
                    - totalRefuelCost.getValue(game);
            int netTime = (game.getGameLength() - freezeTime.getValue(game)) / 1000;
            return (double) (1.0 * scores/netTime);
        }
    };

    public final static GameValueExtractor<Double> calculatedMechanicalCost = new GameValueExtractor<Double>("calculatedMechanicalCost") {
        @Override
        public Double getValue(Game game) {
            return mechanicalCost.getValue(game) + netScore.getValue(game) * (mechanicalTime.getValue(game)/1000) ;
        }
    };

    public final static GameValueExtractor<Integer> numOfAlerts = new GameValueExtractor<Integer>("numOfAlerts") {
        @Override
        public Integer getValue(Game game) {
            return game.numOfAlerts;
        }
    };

    public final static GameValueExtractor<Double> mntAfterAlertDelayMean = new GameValueExtractor<Double>("mntAfterAlertDelayMean") {
        @Override
        public Double getValue(Game game) {
           return game.maintenanceAfterAlertDelay.mean();
        }
    };

    public final static GameValueExtractor<Double> mntAfterAlertDelayVar = new GameValueExtractor<Double>("mntAfterAlertDelayVar") {
        @Override
        public Double getValue(Game game) {
            return game.maintenanceAfterAlertDelay.variance();
        }
    };

    public final static GameValueExtractor<Integer> numOfMaintenanceAfterAlert = new GameValueExtractor<Integer>("numOfMaintenanceAfterAlert") {
        @Override
        public Integer getValue(Game game) {
            return game.numOfMaintenanceAfterAlert;
        }
    };

    public final static GameValueExtractor<Double> malfunctionAfterAlertDelayMean = new GameValueExtractor<Double>("malfunctionAfterAlertDelayMean") {
        @Override
        public Double getValue(Game game) {
            return game.malfunctionAfterAlertDelay.mean();
        }
    };

    public final static GameValueExtractor<Double> malfunctionAfterAlertDelayVar = new GameValueExtractor<Double>("malfunctionAfterAlertDelayVar") {
        @Override
        public Double getValue(Game game) {
            return game.malfunctionAfterAlertDelay.variance();
        }
    };

    public final static GameValueExtractor<Integer> numOfMalfunctionAfterAlert = new GameValueExtractor<Integer>("numOfMalfunctionAfterAlert") {
        @Override
        public Integer getValue(Game game) {
            return game.numOfMalfunctionAfterAlert;
        }
    };

    public final static GameValueExtractor<Integer> firstEventAfterAlertDiff = new GameValueExtractor<Integer>("firstEventAfterAlertDiff") {
        @Override
        public Integer getValue(Game game) {
            return game.numOfMaintenanceAfterAlert - game.numOfMalfunctionAfterAlert;
        }
    };

    public final static GameValueExtractor<Integer> minResponseDelay = new GameValueExtractor<Integer>("minResponseDelay") {
        @Override
        public Integer getValue(Game game) {
            return game.responseDelay == null ? null : game.responseDelay.min;
        }
    };

    public final static GameValueExtractor<Integer> maxResponseDelay = new GameValueExtractor<Integer>("maxResponseDelay") {
        @Override
        public Integer getValue(Game game) {
            return game.responseDelay == null ? null : game.responseDelay.max;
        }
    };

    public final static GameValueExtractor<Double> meanResponseDelay = new GameValueExtractor<Double>("meanResponseDelay") {
        @Override
        public Double getValue(Game game) {
            return game.responseDelay == null ? null : game.responseDelay.mean;
        }
    };

    public final static GameValueExtractor<Double> meanResponseDelay2 = new GameValueExtractor<Double>("meanResponseDelay") {
        @Override
        public Double getValue(Game game) {
            return game.maintenanceAfterAlertDelay.mean();
        }
    };

    public final static GameValueExtractor<Double> varResponseDelay2 = new GameValueExtractor<Double>("varResponseDelay") {
        @Override
        public Double getValue(Game game) {
            return game.maintenanceAfterAlertDelay.variance();
        }
    };

    public final static GameValueExtractor<Integer> minAlertRecommendedTime = new GameValueExtractor<Integer>("minAlertRecommendedTime") {
        @Override
        public Integer getValue(Game game) {
            return game.recommendedTimeForAlert == null ? null : game.recommendedTimeForAlert.min;
        }
    };

    public final static GameValueExtractor<Integer> maxAlertRecommendedTime = new GameValueExtractor<Integer>("maxAlertRecommendedTime") {
        @Override
        public Integer getValue(Game game) {
            return game.recommendedTimeForAlert == null ? null : game.recommendedTimeForAlert.max;
        }
    };

    public final static GameValueExtractor<Double> meanAlertRecommendedTime = new GameValueExtractor<Double>("meanAlertRecommendedTime") {
        @Override
        public Double getValue(Game game) {
            return game.recommendedTimeForAlert == null ? null : game.recommendedTimeForAlert.mean;
        }
    };








    public final static GameValueExtractor<Integer> calculatedScore = new GameValueExtractor<Integer>("totalScore") {
        @Override
        public Integer getValue(Game game) {
            int score = totalShutdown.getValue(game) * Scenario.instance().shutDownScore
                    - totalCollision.getValue(game) * Scenario.instance().collisionCost
                    - mechanicalCost.getValue(game)
                    - totalRefuelCost.getValue(game);
            return score;
        }
    };

    public final static GameValueExtractor<Integer> calculatedScore2 = new GameValueExtractor<Integer>("totalScore") {
        @Override
        public Integer getValue(Game game) {
            return game.getCalculatedScore();
        }
    };

    public final static GameValueExtractor<Integer> gameLength = new GameValueExtractor<Integer>("gameLength") {
        @Override
        public Integer getValue(Game game) {
            return game.getGameLength();
        }
    };

    public final static GameValueExtractor<Double> avgMSE = new GameValueExtractor<Double>("avgMSE") {
        @Override
        public Double getValue(Game game) {
            return game.avgMSE;
        }
    };

    public final static GameValueExtractor<Double> segmentMSE = new GameValueExtractor<Double>("segmentMSE") {
        @Override
        public Double getValue(Game game) {
            return game.segmentMSE;
        }
    };



















}
